package carte.terrain;

import java.awt.Color;
import java.util.ArrayList;

import carte.Carte;
import carte.Cellule;
import carte.Direction;

public class Forest extends Land {
	
	public static final int ALT_MIN = Sand.ALT_MAX;
	public static final int ALT_MAX = Mountain.ALT_MIN + 2;
	//tableau de directions : dans l'ordre bas, gauche, haut, droite
	private static final Direction[] directions = Direction.buildAxes();
	
	//pourcentage initial qu'un arbre soit plante
	public static final float TREE_RATE_INIT = 0.9f;
	//diminution du rate initial avec la distance entre 0 et 1(+ c'est fort et plus ça diminue)
	public static final float DIMINUATION_RATE = 0.4f;
	
	//NOMBRE DE FORETS
	public static final int NBR_FOREST_MIN = 80;
	public static final int NBR_FOREST_MAX = 100;
	
	//TAILLE DE LA FORET
	public static final int SIZE_FOREST_MIN = 20;
	public static final int SIZE_FOREST_MAX = 50;
	
	//l'écart minimal entre deux arbres pour qu'un arbre puisse exister
	public static final int RANGE_SPLIT_TREES = 2;
	
	protected static Forest _instance;

	public Forest() {
		texture = new Color(41, 112, 2);
		altitudeMin = ALT_MIN;
		altitudeMax = ALT_MAX;
	}

	public static Forest build() {
		if (null == _instance) {
			_instance = new Forest();
		}
		return _instance;
	}
	
	public static void generateForest(Carte map){
		//deja on va generer une position initiale
		int x,y;
		//position de la case parcourue
		int i,j;
		//on commence vers le bas
		int direction;
		int step;
		Cellule[][] grid = map.getMap();
		ArrayList<Cellule> cells;
		//nombre d'arbre autour
		int nbreTreesAround;
		//taux suivant lequel l'arbre va etre place ou non
		float placementRate;
		//distance entre la case visitee et la source de la foret
		float distance;
		//taille de la foret
		int sizeForest;
		//nombre de forets
		int nbrForest;
		
		nbrForest = rand.nextInt(NBR_FOREST_MAX - NBR_FOREST_MIN) + NBR_FOREST_MIN;
		//on genere nbForest Forets
		do{
			//de taille sizeForest
			sizeForest = rand.nextInt(SIZE_FOREST_MAX-SIZE_FOREST_MIN) + SIZE_FOREST_MIN;
			step = 1;
			direction = 0;
			do{
				x = rand.nextInt(map.getMap().length);
				y = rand.nextInt(map.getMap().length);
			}while(ALT_MIN > grid[x][y].getAltitude() || ALT_MAX < grid[x][y].getAltitude());
			//on souhaite pouvoir poser une foret sur notre position initiale
			grid[x][y].setLand(Land.Forest());
			//on initialise i et j
			i = x;
			j = y;
			//maintenant on va tourner en escargot autour de cette case
			do{
				//on change deux fois de direction sans augmenter le step pour aller en escargot
				for(int cpt=1; cpt<=2; cpt++){
					//on avance autant de fois que le step dans la direction actuelle et on regarde si on pose une foret dessus
					for(int cpt2=1; cpt2<=step; cpt2++){
						i += directions[direction].getDx();
						j += directions[direction].getDy();
						//on va verifier que la case existe
						if ((i >= 0) && (j >= 0) && (i < map.getWidth()) && (j < map.getHeight())){
							//on recupere les cellules adjacentes a notre nouvelle cellule
							cells = map.getCellRange(i, j, RANGE_SPLIT_TREES);
							nbreTreesAround = 0;
							//on compte le nombre d'arbres autour
							for(int cpt3=1; cpt3<cells.size(); cpt3++){
								if(cells.get(cpt3).getLand() instanceof Forest){
									nbreTreesAround ++;
								}
							}
							if(nbreTreesAround != 0){
								//s'il y a au moins un arbre autour, alors on a une chance de placer un arbre ici s'il peut être place
								if(ALT_MIN <= grid[i][j].getAltitude() && ALT_MAX >= grid[i][j].getAltitude()){
									//on va calculer le taux pour savoir si l'arbre va ou non etre place
									distance = computeDistance(x, y, i, j);
									//en-dessous d'une distance de deux ce calcul n'a aucune signification
									if(distance >= 2){
										placementRate = TREE_RATE_INIT/(float)(distance*DIMINUATION_RATE);
									}
									else{
										placementRate = TREE_RATE_INIT;
									}
									if(rand.nextFloat() < placementRate){
										grid[i][j].setLand(Land.Forest());
									}
								}
							}
						}
					}
					direction = (direction + 1) % directions.length;
				}
				step ++;
			}while(step <= sizeForest);
			nbrForest--;
		}while(nbrForest>0);
	}
	
	// donne la distance entre le point x,y et le point i,j
	private static float computeDistance(int x, int y, int i, int j) {
		float distance = 0.0f;
		distance = (float)Math.sqrt( (double)((x-i) * (x-i)) + (double)((y-j) * (y-j)) );
		return distance;
	}

}
